package biz.datalk.industrialland.admin.modules.system.controller;

import biz.datalk.commons.utils.BasicParserUtils;
import biz.datalk.commons.utils.FileUtil;
import biz.datalk.industrialland.admin.config.properties.CoreFileProperties;
import biz.datalk.industrialland.admin.modules.system.service.ICommonService;
import biz.datalk.industrialland.common.def.CommonConstant;
import biz.datalk.industrialland.common.exception.ApplicationException;
import biz.datalk.industrialland.common.result.UnifyResult;
import biz.datalk.industrialland.common.result.UnifyResultUtil;
import biz.datalk.industrialland.common.util.RedisUtil;
import biz.datalk.industrialland.common.util.SpringContextUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author tarofang@163.com
 * @date 2019-12-24
 */
@Slf4j
@RestController
@RequestMapping("/sys/common")
public class CommonController {

    @Value(value = "${core.file.path.upload}")
    private String uploadPath;

    @Value(value = "${core.file.view-prefix}")
    private String previewPrefix;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private CoreFileProperties coreFileProperties;

    private ICommonService commonService;
    private boolean commonServiceInit = false;

    private void lazyInitCommonService() {
        // CommonController 实例是 spring 容器在管理且是单例，故这里初始化时无需考虑线程安全问题
        if (commonServiceInit) {
            return;
        }
        try {
            String beanName = StringUtils.trimToNull(coreFileProperties.getUploadServiceSpringBeanName());
            if (beanName == null) {
                this.commonServiceInit = true;
                return;
            }
            this.commonService = SpringContextUtils.getBean(beanName, ICommonService.class);
        } catch (Exception ex) {
            log.warn("{}", ex.getMessage(), ex);
        }
        this.commonServiceInit = true;
    }


    @PostMapping(value = "/upload/{type}")
    public UnifyResult upload(@PathVariable("type") String type, HttpServletRequest request) {
        lazyInitCommonService();
        if (coreFileProperties.isUploadApiUseCustom()) {
            return commonService.upload(type, request);
        }
        type = getType(type);
        String ymd = getYmd();
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        if (multipartRequest == null) {
            return UnifyResult.fail("不支持文件上传");
        }

        String hasFullPath = multipartRequest.getParameter(CommonConstant.UPLOAD_FILE_RETURN_PREVIEW_FLAG_KEY);
        String contextPath = request.getContextPath();

        // 获取上传文件对象
        MultipartFile mf = multipartRequest.getFile("file");
        if (mf == null) {
            return UnifyResult.fail("未上传文件");
        }

        String validExtensionKey = "sys.upload.valid.extension." + type;
        String maxSizeKey = "sys.upload.max.size." + type;
        String validExtensionStr = redisUtil.getStr(validExtensionKey, "").trim();
        if (StringUtils.isEmpty(validExtensionStr)) {
            return UnifyResult.fail("不支持上传该格式文件");
        }
        String[] validExtensionArr = StringUtils.splitByWholeSeparatorPreserveAllTokens(validExtensionStr, ",");

        String maxSizeStr = redisUtil.getStr(maxSizeKey, "").trim();
        long maxSize = BasicParserUtils.parseLong(maxSizeStr, 5 * 1024 * 1024L);

        UnifyResult result = saveFile(mf, type, ymd, validExtensionArr, maxSize);
        if (UnifyResultUtil.isFail(result)) {
            return result;
        }

        String dbpath = UnifyResultUtil.getData(result, String.class);

        if (StringUtils.equals(hasFullPath, "Y")) {
            return UnifyResult.success().mapData().put("path", dbpath).put("preview", getPreviewPath(dbpath, contextPath));
        }

        return UnifyResult.success().setData(dbpath);
    }

    @PostMapping(value = "/uploadbatch/{type}")
    public UnifyResult uploadbatch(@PathVariable("type") String type, HttpServletRequest request) {
        lazyInitCommonService();
        if (coreFileProperties.isUploadbatchApiUseCustom()) {
            return commonService.uploadbatch(type, request);
        }

        type = getType(type);
        String ymd = getYmd();
        //File baseDir = getBaseDir(type, ymd);

        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        if (multipartRequest == null) {
            return UnifyResult.fail("不支持文件上传");
        }
        String hasFullPath = multipartRequest.getParameter(CommonConstant.UPLOAD_FILE_RETURN_PREVIEW_FLAG_KEY);
        String contextPath = request.getContextPath();

        List<MultipartFile> mfList = multipartRequest.getFiles("file");
        if (CollectionUtils.isEmpty(mfList)) {
            return UnifyResult.fail("未上传文件");
        }

        String validExtensionKey = "sys.upload.valid.extension." + type;
        String maxSizeKey = "sys.upload.max.size." + type;
        String validExtensionStr = redisUtil.getStr(validExtensionKey, "").trim();
        if (StringUtils.isEmpty(validExtensionStr)) {
            return UnifyResult.fail("不支持上传该格式文件");
        }
        String[] validExtensionArr = StringUtils.splitByWholeSeparatorPreserveAllTokens(validExtensionStr, ",");

        String maxSizeStr = redisUtil.getStr(maxSizeKey, "").trim();
        long maxSize = BasicParserUtils.parseLong(maxSizeStr, 5 * 1024 * 1024L);

        UnifyResult result = null;
        List<String> dbPathList = new ArrayList<>();
        for (MultipartFile mf : mfList) {
            result = saveFile(mf, type, ymd, validExtensionArr, maxSize);
            if (UnifyResultUtil.isFail(result)) {
                return result;
            }
            dbPathList.add(UnifyResultUtil.getData(result, String.class));
        }

        if (StringUtils.equals(hasFullPath, "Y")) {
            List<Map<String, String>> resFilePaths = new ArrayList<>();
            Map<String, String> tmpMap;
            for (String path : dbPathList) {
                tmpMap = new HashMap<>(4);
                tmpMap.put("path", path);
                tmpMap.put("preview", getPreviewPath(path, contextPath));
                resFilePaths.add(tmpMap);
            }
            return UnifyResult.success().setData(resFilePaths);
        }

        return UnifyResult.success().setData(dbPathList);
    }

    private String getPreviewPath(String relativePath, String contextPath) {
        String ctxp = "/".equals(contextPath) ? "" : contextPath;
        String path = relativePath.startsWith("/") ? relativePath.substring(1) : relativePath;
        String prefix = StringUtils.trimToEmpty(previewPrefix);
        prefix = prefix.endsWith("/") ? prefix.substring(0, prefix.length() - 1) : prefix;
        return prefix + ctxp + "/sys/common/view/" + path;
    }

    private UnifyResult saveFile(MultipartFile mf, String type, String ymd, String[] validExtensions, long maxSize) {
        if (coreFileProperties.isSaveFileFunctionUseCustom()) {
            return commonService.saveFile(mf, type, ymd, validExtensions, maxSize);
        }

        String origName = mf.getOriginalFilename();
        String extension = FilenameUtils.getExtension(origName);
        if (extension == null) {
            return UnifyResult.fail("不支持该类型的文件");
        }
        if (!FileUtil.isValidFile(origName, validExtensions)) {
            return UnifyResult.fail(String.format("不支持该类型文件[%s]", extension));
        }
        if (mf.getSize() > maxSize) {
            return UnifyResult.fail(String.format("上传文件过大[%s]，超过[%s]", FileUtil.formatFileSizes(mf.getSize(), "#.##"), FileUtil.formatFileSizes(maxSize, "#.##")));
        }
        String baseName = FilenameUtils.getBaseName(origName);
        String fileName = baseName + "_" + System.currentTimeMillis() + FilenameUtils.EXTENSION_SEPARATOR + extension;
        File savefile = new File(getBaseDir(type, ymd), fileName);
        try {
            mf.transferTo(savefile);
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
            return UnifyResult.fail("保存文件失败");
        }

        String dbpath = type + File.separator + ymd + File.separator + fileName;
        dbpath = FileUtil.formatPath(dbpath, false, '/');
        return UnifyResult.success().setData(dbpath);
    }

    private File getBaseDir(String type, String ymd) {
        type = StringUtils.isNotBlank(type) ? type.trim() : "file";
        String baseDirPath = uploadPath + File.separator + type + File.separator + ymd;
        File baseDir = new File(baseDirPath);
        if (!baseDir.exists()) {
            if (!baseDir.mkdirs()) {
                log.error("create directory fail: {}", baseDir.getAbsolutePath());
                throw new ApplicationException("create directory fail");
            }
        }
        return baseDir;
    }

    private String getYmd() {
        return getYmd(CommonConstant.UPLOAD_FILE_PATH_DATE_SEPARATOR_PATTERN);
    }

    private String getYmd(String pattern) {
        pattern = StringUtils.isBlank(pattern) ? CommonConstant.UPLOAD_FILE_PATH_DATE_SEPARATOR_PATTERN : pattern.trim();
        return LocalDate.now().format(DateTimeFormatter.ofPattern(pattern));
    }

    private String getType(String type) {
        type = StringUtils.isNotBlank(type) ? type.trim() : "file";
        return type;
    }

    /**
     * 预览图片
     * 请求地址：http://localhost:8080/common/view/{user/20190119/e1fe9925bc315c60addea1b98eb1cb1349547719_1547866868179.jpg}
     *
     * @param request  {@link javax.servlet.http.HttpServletRequest}
     * @param response {@link javax.servlet.http.HttpServletResponse}
     *
     * @author tarofang@163.com
     * @date 2019-12-24
     */
    @GetMapping(value = "/view/**")
    public void view(HttpServletRequest request, HttpServletResponse response) {
        lazyInitCommonService();
        if (coreFileProperties.isViewApiUseCustom()) {
            commonService.view(request, response);
            return;
        }

        // ISO-8859-1 ==> UTF-8 进行编码转换
        String imgPath = extractPathFromPattern(request);
        // 其余处理略
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            imgPath = imgPath.replace("..", "");
            if (imgPath.endsWith(",")) {
                imgPath = imgPath.substring(0, imgPath.length() - 1);
            }
            response.setContentType("image/jpeg;charset=utf-8");
            String localPath = uploadPath;
            String imgurl = localPath + File.separator + imgPath;
            inputStream = new BufferedInputStream(new FileInputStream(imgurl));
            outputStream = response.getOutputStream();
            byte[] buf = new byte[1024];
            int len;
            while ((len = inputStream.read(buf)) > 0) {
                outputStream.write(buf, 0, len);
            }
            response.flushBuffer();
        } catch (IOException e) {
            log.error("预览图片失败" + e.getMessage());
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            }
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            }
        }

    }

    /**
     * 下载文件
     * 请求地址：http://localhost:8080/common/download/{user/20190119/e1fe9925bc315c60addea1b98eb1cb1349547719_1547866868179.jpg}
     *
     * @param request  {@link javax.servlet.http.HttpServletRequest}
     * @param response {@link javax.servlet.http.HttpServletResponse}
     *
     * @author tarofang@163.com
     * @date 2019-12-24
     */
    @GetMapping(value = "/download/**")
    public void download(HttpServletRequest request, HttpServletResponse response) {
        lazyInitCommonService();
        if (coreFileProperties.isDownloadApiUseCustom()) {
            commonService.download(request, response);
            return;
        }

        // ISO-8859-1 ==> UTF-8 进行编码转换
        String filePath = extractPathFromPattern(request);
        // 其余处理略
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            filePath = filePath.replace("..", "");
            if (filePath.endsWith(",")) {
                filePath = filePath.substring(0, filePath.length() - 1);
            }
            String localPath = uploadPath;
            String downloadFilePath = localPath + File.separator + filePath;
            File file = new File(downloadFilePath);
            if (file.exists()) {
                // 设置强制下载不打开
                response.setContentType("application/force-download");
                String fname = new String(file.getName().getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
                response.addHeader("Content-Disposition", "attachment;fileName=" + fname);
                inputStream = new BufferedInputStream(new FileInputStream(file));
                outputStream = response.getOutputStream();
                byte[] buf = new byte[1024];
                int len;
                while ((len = inputStream.read(buf)) > 0) {
                    outputStream.write(buf, 0, len);
                }
                response.flushBuffer();
            }

        } catch (Exception e) {
            log.info("文件下载失败" + e.getMessage());
            // e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }


    public static String extractPathFromPattern(final HttpServletRequest request) {
        String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
        String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
        return new AntPathMatcher().extractPathWithinPattern(bestMatchPattern, path);
    }
}
